package com.tree.springaop.aop;

import com.alibaba.fastjson.JSON;
import com.tree.springaop.annotation.WebLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @ClassName com.tree.springaop.aop
 * Description: <类功能描述>. <br>
 * <p>
 *     注意执行顺序 ：
 *     正常执行：前置通知-》目标方法-》后置通知-》返回通知
 *     出现异常：前置通知-》目标方法-》后置通知-》异常通知
 *     正常执行：around begin-->before --> target method--> around after --> after --> after returning
 *     若抛出异常：around begin-->before --> target method --> after--> throw
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Around Before........................begin execute method
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - ========================================== Start begin==========================================
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Before........................
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - URL            : http://localhost:8080/user/login
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Description    : 请求了用户登录接口
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - HTTP Method    : GET
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Class Method   : com.tree.springaop.controller.UserController.findUserInfo
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - IP             : 127.0.0.1
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Request Args   : ["login"]
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - ========================================== Start end==========================================
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.c.UserController - find user info ...
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - ===========Around after Begin==============
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Response Result : "success"
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Time-Consuming : 37ms
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - Around End........................
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - ===========Around after End==============
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - After........................
2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - ========AFTER END=========

2020-01-30 12:52:03 [http-nio-8080-exec-1] INFO  c.t.s.a.WebLogAopConfig - =========After Returning.......return result=success==========
 * </p>
 * @Author tree
 * @Date 2019-06-09 11:50
 * @Version 1.0
 */
@Aspect
@Configuration
//只在dev、test下生效
//@Profile({"dev", "test"})
public class WebLogAopConfig {
    private Logger logger = LoggerFactory.getLogger(WebLogAopConfig.class);
    private static final String LINE_SEPARATOR = System.lineSeparator();


    /**
     * 以定义@WebLog注解为切点
     */
    @Pointcut("@annotation(com.tree.springaop.annotation.WebLog)")
    public void webLog(){}

    @Before("webLog()")
    public void  doWebLogBefore(JoinPoint joinPoint) throws ClassNotFoundException {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取method描述
        String methodDescription = getMethodDescription(joinPoint);
        // 打印请求相关参数
        logger.info("========================================== Start begin==========================================");
        logger.info("Before........................");
        // 打印请求 url
        logger.info("URL            : {}", request.getRequestURL().toString());
        // 打印描述信息
        logger.info("Description    : {}", methodDescription);
        // 打印 Http method
        logger.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求的 IP
        logger.info("IP             : {}", request.getRemoteAddr());
        // 打印请求入参
        logger.info("Request Args   : {}", JSON.toJSONString(joinPoint.getArgs()));
        logger.info("========================================== Start end==========================================");

    }



    /**
     * WebLog环绕
     * @return
     */
    @Around("webLog()")
    public Object doWebLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        logger.info("===========Around Before........................begin execute method");

        Object o = joinPoint.proceed();
        logger.info("===========Around after Begin==============");
        //打印结果
        logger.info("Response Result : {}", JSON.toJSONString(o));
        //打印耗时
        logger.info("Time-Consuming : {}ms",(System.currentTimeMillis()-startTime));
        logger.info("Around End........................");
        logger.info("===========Around after End==============");


        return o;


    }

    @After("webLog()")
    public void doWebLogAfter(){
        logger.info("After........................");

        logger.info("========AFTER END=========" + LINE_SEPARATOR);
    }


    /**
     * 可以获取返回的值
     * @param returnVal
     */
    @AfterReturning(pointcut = "webLog()",returning = "returnVal")
    public void doWebLogAfterReturning(Object returnVal){
        logger.info("=========After Returning.......return result={}==========",returnVal);

    }

    /**
     * 可以获取抛出的异常
     * 注意throwing 的名字必须与方法参数名一致
     */
    @AfterThrowing(pointcut = "webLog()", throwing = "ex")
    public void doWebLogAfterThrowing(Exception ex){
        logger.info("==============After Throwing........................");

    }


    /**
     * 获取Method描述
     * 根据jointpoint获取class与method，并根据method获取annotation
     * @param joinPoint
     * @return
     * @throws ClassNotFoundException
     */
    private String getMethodDescription(JoinPoint joinPoint) throws ClassNotFoundException {

        String methodName = joinPoint.getSignature().getName();
        Class target = joinPoint.getTarget().getClass();
        String description = "";
        Method[] methods = target.getMethods();
        for (Method method : methods) {
            if(method.getName().equalsIgnoreCase(methodName)){
                description = method.getAnnotation(WebLog.class).description();
                break;
            }

        }
        return description;
    }
}
